#include "thread.h"
#include "synch.h"
#include "system.h"

// constant number for prices
#define TICKET_PRICE 12
#define POPCORN_PRICE 5
#define SODA_PRICE 4
#define MAX_CLERK 5
#define MAX_TICKET_TAKER 3
#define SEATS 25

// a customer info
// used to passing to the customerBehavior
struct CustomerInfo {
	int customerNumber;
	int groupNumber;
	int groupSize;
};

// both for ticket clerks, concession clerks and ticket takers
enum ClerkState {
	C_READY, C_BUSY, C_BREAK
};

enum MovieState {
	M_WAITING, M_STARTED, M_FINISHED, M_INIT
};

// for shared resources
Semaphore *incomeMutex;
Semaphore *seatMutex;
Semaphore *leaveMutex;
int totalIncome;
int seatRow[5];

// number of each thread
int numberOfCustomers;
int numberOfGroups;
int numberOfTicketClerks;
int numberOfConcessionClerks;
int numberOfTicketTakers;

// for group commucation
// make sure the group move together
Semaphore **groupSemaphore;
Semaphore **groupBathroomSemaphore;
Lock **groupLock;
Condition **groupCV;
int *customerPopcorn;
int *customerSoda;
int *customerBathroomNumber;
int *customerSeat;
int leaveCount;

// for buy tickets
Lock *tcLineLock;
Lock *tcLock[MAX_CLERK];
Condition *tcLineCV[MAX_CLERK];
Condition *tcCV[MAX_CLERK];
int tcTicket[MAX_CLERK];
int tcPrice[MAX_CLERK];
int tcLineCount[MAX_CLERK];
int tcIncome[MAX_CLERK];
ClerkState tcState[MAX_CLERK];
int tcBreak[MAX_CLERK];
Condition *tcBreakCV;
int tcBreakTime[MAX_CLERK];

// for buy food
Lock *ccLineLock;
Lock *ccLock[MAX_CLERK];
Condition *ccLineCV[MAX_CLERK];
Condition *ccCV[MAX_CLERK];
int ccPopcorn[MAX_CLERK];
int ccSoda[MAX_CLERK];
int ccPrice[MAX_CLERK];
int ccLineCount[MAX_CLERK];
int ccIncome[MAX_CLERK];
ClerkState ccState[MAX_CLERK];
int ccBreak[MAX_CLERK];
Condition *ccBreakCV;
int ccBreakTime[MAX_CLERK];

// for ticket taker
Lock *ttLineLock;
Lock *ttLock[MAX_TICKET_TAKER];
Condition *ttLineCV[MAX_TICKET_TAKER];
Condition *ttCV[MAX_TICKET_TAKER];
int ttLineCount[MAX_TICKET_TAKER];
int ttTicket[MAX_TICKET_TAKER];
int ttStop[MAX_TICKET_TAKER];
int ttProcess[MAX_TICKET_TAKER];
int ttRemainSeat;
int ttTicketTotal;
ClerkState ttState[MAX_TICKET_TAKER];
int ttBreak[MAX_TICKET_TAKER];
Condition *ttBreakCV;
int ttBreakTime[MAX_TICKET_TAKER];

// for playing the movie
int customerReadyWatchingMovie;
Lock *movieLock;
Condition *movieCV;
Lock *mtLock;
Condition *mtCV;
MovieState movieState;

// for lobby
Condition *lobbyCV;

// for bathroom
Semaphore *bathroomSemaphore;

// initialize all locks, condition variables, 
// monitor variables and shared resources
void Init() {
	totalIncome = 0;
	leaveMutex = new Semaphore("Leaving Mutex", 1);
	seatMutex = new Semaphore("Seat Mutex", 1);
	leaveCount = 0;
	ttRemainSeat = SEATS;
	ttTicketTotal = 0;
	movieLock = new Lock("Movie Lock");
	movieCV = new Condition("Movie CV");
	mtLock = new Lock("MT Lock");
	mtCV = new Condition("MT CV");
	lobbyCV = new Condition("Lobby CV");
	movieState = M_INIT;
	ttLineLock = new Lock("TT Line Lock");
	customerSeat = new int[numberOfCustomers];
	incomeMutex = new Semaphore("Income Mutex", 1);
	tcLineLock = new Lock("TC Line Lock");
	ccLineLock = new Lock("CC Line Lock");	
	groupSemaphore = new Semaphore*[numberOfGroups];
	groupBathroomSemaphore = new Semaphore*[numberOfGroups];
	groupLock = new Lock*[numberOfGroups];
	groupCV = new Condition*[numberOfGroups];
	customerBathroomNumber = new int[numberOfGroups];
	customerPopcorn = new int[numberOfCustomers];
	customerSoda = new int[numberOfCustomers];
	bathroomSemaphore = new Semaphore("Bathroom Semaphore", 1);
	tcBreakCV = new Condition("TC BREAK CV");
	ccBreakCV = new Condition("CC BREAK CV");
	ttBreakCV = new Condition("TT BREAK CV");
	for(int i = 0; i < 5; i++) {
		seatRow[i] = 5;
	}
	for(int i = 0; i < numberOfGroups; i++) {
		groupSemaphore[i] = new Semaphore("Group Semaphore", 0);
		groupBathroomSemaphore[i] = new Semaphore("Group Bathroom Semaphore", 0);
		groupLock[i] = new Lock("Group Lock");
		groupCV[i] = new Condition("Group CV");
		customerBathroomNumber[i] = 0;
	}
	for(int i = 0; i < numberOfCustomers; i++) {
		customerPopcorn[i] = 0;
		customerSoda[i] = 0;
		customerSeat[i] = -1;
	}
	for(int i = 0; i < numberOfTicketClerks; i++) {
		tcLock[i] = new Lock("TC Lock");
		tcLineCV[i] = new Condition("TC Line CV");
		tcCV[i] = new Condition("TC CV");
		tcLineCount[i] = 0;
		tcTicket[i] = 0;
		tcPrice[i] = 0;
		tcIncome[i] = 0;
		tcState[i] = C_BUSY;
		tcBreak[i] = 0;
		tcBreakTime[i] = 0;
	}
	for(int i = 0; i < numberOfConcessionClerks; i++) {
		ccLock[i] = new Lock("CC Lock");
		ccLineCV[i] = new Condition("CC Line CV");
		ccCV[i] = new Condition("CC CV");
		ccLineCount[i] = 0;
		ccPopcorn[i] = 0;
		ccSoda[i] = 0;
		ccPrice[i] = 0;
		ccIncome[i] = 0;
		ccState[i] = C_BUSY;
		ccBreak[i] = 0;
		ccBreakTime[i] = 0;
	}
	for(int i = 0; i < numberOfTicketTakers; i++) {
		ttLock[i] = new Lock("TT Lock");
		ttLineCV[i] = new Condition("TT Line CV");
		ttCV[i] = new Condition("TT CV");
		ttLineCount[i] = 0;
		ttTicket[i] = 0;
		ttStop[i] = 0;
		ttState[i] = C_BUSY;
		ttBreak[i] = 0;
		ttBreakTime[i] = 0;
	}
}

/**
 * this function is the simulation of a regular customer
 * each customer is in a specific group
 * group members move together
 * they holds until the head customer tells to go
 * customers only cummucate with head customers
 **/
void CustomerBehavior(int data) {
	// get the customer info
	CustomerInfo *myData = (CustomerInfo *)data;
	int customerNumber = myData->customerNumber;
	int groupNumber = myData->groupNumber;
	
	// assume a customer arrives theater at a ramdom time
	int arriveTime = rand() % 100;
	for(int i = 0; i < arriveTime; i++) {
		currentThread->Yield();
	}
	
	// the customer arrives
	printf("Customer %d in Group %d has entered the movie theater.\n",
		customerNumber, groupNumber);
	// the customer tells the head customer he/she arrives
	groupLock[groupNumber]->Acquire();
	groupSemaphore[groupNumber]->V();
	
	// then wait for the head customer to buy the ticket
	printf("Customer %d of Group %d is waiting for the HeadCustomer.\n",
		customerNumber, groupNumber);
	groupCV[groupNumber]->Wait(groupLock[groupNumber]);
	printf("Customer %d of Group %d has been told by the HeadCustomer to proceed.\n",
		customerNumber, groupNumber);
	// decide if they want food
	int popcorn = rand() % 4;
	int soda = rand() % 4;
	// tell the head if the customer wants any food
	if (popcorn != 0) {
		customerPopcorn[customerNumber] = 1;
	}
	if (soda != 0) {
		customerSoda[customerNumber] = 1;
	}
	printf("Customer %d in Group %d wants %d popcorn and %d soda.\n",
		customerNumber, groupNumber, customerPopcorn[customerNumber],
		customerSoda[customerNumber]);
	groupSemaphore[groupNumber]->V();
	// wait the head to buy food
	printf("Customer %d of Group %d is waiting for the HeadCustomer.\n",
		customerNumber, groupNumber);
	groupCV[groupNumber]->Wait(groupLock[groupNumber]);
	printf("Customer %d of Group %d has been told by the HeadCustomer to proceed.\n",
		customerNumber, groupNumber);
	
	while (customerSeat[customerNumber] == -1) {
		// there's no seat, go to lobby
		printf("Customer %d in Group %d is in the lobby.\n", customerNumber, groupNumber);
		groupSemaphore[groupNumber]->V();
		printf("Customer %d of Group %d is waiting for the HeadCustomer.\n", customerNumber, groupNumber);
		groupCV[groupNumber]->Wait(groupLock[groupNumber]);
		printf("Customer %d of Group %d has been told by the HeadCustomer to proceed.\n",
			customerNumber, groupNumber);
		printf("Customer %d in Group %d is leaving the lobby.\n", customerNumber, groupNumber);
		groupSemaphore[groupNumber]->V();
		printf("Customer %d of Group %d is waiting for the HeadCustomer.\n",
			customerNumber, groupNumber);
		groupCV[groupNumber]->Wait(groupLock[groupNumber]);
		printf("Customer %d of Group %d has been told by the HeadCustomer to proceed.\n",
			customerNumber, groupNumber);
	}
	printf("Customer %d in Group %d has found the following seat: row %d and seat %d.\n",
		customerNumber, groupNumber, customerSeat[customerNumber]/5, customerSeat[customerNumber]%5);
	groupLock[groupNumber]->Release();
	
	// see the movie
	movieLock->Acquire();
	printf("Customer %d in Group %d in sitting in a theater room seat.\n",
		customerNumber, groupNumber);
	customerReadyWatchingMovie++;
	movieCV->Wait(movieLock);
	customerReadyWatchingMovie--;
	printf("Customer %d in Group %d in getting out of a theater room seat.\n",
		customerNumber, groupNumber);
	movieLock->Release();
			
	// tell the head if the customer wants to go to the bathroom
	groupLock[groupNumber]->Acquire();
	int bathroom = rand() % 4;
	if (bathroom == 0) {
		customerBathroomNumber[groupNumber]++;
	}
	groupLock[groupNumber]->Release();
	if (bathroom == 0) {
		// go to the bathroom
		groupSemaphore[groupNumber]->V();
		bathroomSemaphore->P();
		printf("Customer %d in Group %d is going to the bathroom.\n",
			customerNumber, groupNumber);
		int bathroomTime = rand() % 5;
		for(int i = 0; i < bathroomTime; i++) {
			currentThread->Yield();
		}
		printf("Customer %d in Group %d is leaving to the bathroom.\n",
			customerNumber, groupNumber);
		bathroomSemaphore->V();
		groupLock[groupNumber]->Acquire();
		groupBathroomSemaphore[groupNumber]->V();
	} else {
		groupLock[groupNumber]->Acquire();
		groupSemaphore[groupNumber]->V();
	}
	
	// wait for every one comes out of the bathroom
	printf("Customer %d in Group %d is in the lobby.\n", customerNumber, groupNumber);
	printf("Customer %d of Group %d is waiting for the HeadCustomer.\n",
		customerNumber, groupNumber);
	groupCV[groupNumber]->Wait(groupLock[groupNumber]);
	printf("Customer %d of Group %d has been told by the HeadCustomer to proceed.\n",
		customerNumber, groupNumber);
	printf("Customer %d in Group %d is leaving the lobby.\n",customerNumber, groupNumber);
	groupLock[groupNumber]->Release();
	
	// all set, ready to leave
	// the customer leaves
	leaveMutex->P();
	leaveCount++;
	printf("Customer %d in Group %d has left the movie theater.\n", 
		customerNumber, groupNumber);
	leaveMutex->V();
}

/*
 * HeadCustomerBehavior
 * group leader
 * commucate with all employees
 * make groups move together
 */
void HeadCustomerBehavior(int data) {
	// get the customer info
	CustomerInfo *myData = (CustomerInfo *)data;
	int customerNumber = myData->customerNumber;
	int groupNumber = myData->groupNumber;
	int groupSize = myData->groupSize;
	
	// assume a customer arrives theater at a ramdom time
	int arriveTime = rand() % 100;
	for(int i = 0; i < arriveTime; i++) {
		currentThread->Yield();
	}
	
	// the customer arrives
	printf("Customer %d in Group %d has entered the movie theater.\n",
		customerNumber, groupNumber);
	// the head customer waits for every to arrive
	printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
		customerNumber, groupNumber);
	for(int i = 1; i < groupSize; i++) {
		groupSemaphore[groupNumber]->P();
	}
	
	// buy the tickets
	int myTicketClerk = -1;
	tcLineLock->Acquire();
	while (myTicketClerk == -1 || tcState[myTicketClerk] == C_BREAK) {
		if (myTicketClerk != -1) {
			printf("Customer %d in Group %d sees TicketClerk %d is on break.\n",
				customerNumber, groupNumber, myTicketClerk);
		}
		// fine a ready tc
		myTicketClerk = -1;
		for(int i = 0; i < numberOfTicketClerks; i++) {
			if (tcState[i] == C_READY) {
				myTicketClerk = i;
				tcState[i] = C_BUSY;
				break;
			}
		}
		
		if (myTicketClerk == -1) {
			// no one's ready
			// get in the shortest line
			int minLineNumber;
			for(int i = 0; i < numberOfTicketClerks; i++) {
				if (tcState[i] != C_BREAK) {
					// there must be at least one tc who's not on break.
					minLineNumber = i;
					break;
				}
			}
			int minLineCount = tcLineCount[minLineNumber];
			for(int i = minLineNumber+1; i < numberOfTicketClerks; i++) {
				if (tcState[i] != C_BREAK) {
					if (minLineCount > tcLineCount[i]) {
						minLineNumber = i;
						minLineCount = tcLineCount[i];
					}
				}
			}
			myTicketClerk = minLineNumber;
			tcLineCount[myTicketClerk]++;
			printf("Customer %d in Group %d is getting in TicketClerk line %d.\n",
				customerNumber, groupNumber, myTicketClerk);
			int times = tcBreakTime[myTicketClerk];
			tcLineCV[myTicketClerk]->Wait(tcLineLock);
			if (times != tcBreakTime[myTicketClerk]) {
				printf("Customer %d in Group %d sees TicketClerk %d is on break.\n",
					customerNumber, groupNumber, myTicketClerk);
				myTicketClerk = -1;
				continue;
			}
		}
	}
	tcLineLock->Release();
	// interact with tc
	tcLock[myTicketClerk]->Acquire();
	// tell tc how many tickets
	printf("Customer %d in Group %d is walking up to TicketClerk %d to buy %d tickets.\n", 
		customerNumber, groupNumber, myTicketClerk, groupSize);
	tcTicket[myTicketClerk] = groupSize;
	tcCV[myTicketClerk]->Signal(tcLock[myTicketClerk]);
	tcCV[myTicketClerk]->Wait(tcLock[myTicketClerk]);
	int price = tcPrice[myTicketClerk];
	// pay
	printf("Customer %d in Group %d in TicketClerk line %d is paying $%.2lf for tickets.\n",
		customerNumber, groupNumber, myTicketClerk, (double)price);
	tcCV[myTicketClerk]->Signal(tcLock[myTicketClerk]);
	tcCV[myTicketClerk]->Wait(tcLock[myTicketClerk]);
	// leave
	printf("Customer %d in Group %d is leaving TicketClerk %d.\n",
		customerNumber, groupNumber, myTicketClerk);
	tcCV[myTicketClerk]->Signal(tcLock[myTicketClerk]);
	tcLock[myTicketClerk]->Release();
	
	// wake up all customers then wait for them to decide if they want food
	groupLock[groupNumber]->Acquire();
	printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
		customerNumber, groupNumber);
	groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
	groupLock[groupNumber]->Release();	
	// wait for every customer to tell him the food status
	printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
		customerNumber, groupNumber);
	for(int i = 1; i < groupSize; i++) {
		groupSemaphore[groupNumber]->P();
	}
	
	groupLock[groupNumber]->Acquire();
	int popcorn = rand() % 4;
	int soda = rand() % 4;
	// tell the head if the customer wants any food
	if (popcorn != 0) {
		customerPopcorn[customerNumber] = 1;
	}
	if (soda != 0) {
		customerSoda[customerNumber] = 1;
	}
	groupLock[groupNumber]->Release();
	printf("Customer %d in Group %d wants %d popcorn and %d soda.\n",
		customerNumber, groupNumber, customerPopcorn[customerNumber],
		customerSoda[customerNumber]);
	
	groupLock[groupNumber]->Acquire();
	int popcornForGroup = 0;
	int sodaForGroup = 0;
	// get the food request from the group members
	for(int i = 0; i < groupSize; i++) {
		printf("Customer %d in Group %d has %d popcorn and %d soda request from a group member.\n",
			customerNumber, groupNumber, customerPopcorn[customerNumber+i], 
			customerSoda[customerNumber+i]);
		popcornForGroup += customerPopcorn[customerNumber+i];
		sodaForGroup += customerSoda[customerNumber+i];
	}
	groupLock[groupNumber]->Release();
	// buy food
	// get in line
	int myConcessionClerk = -1;
	ccLineLock->Acquire();
	while (myConcessionClerk == -1 || ccState[myConcessionClerk] == C_BREAK) {
		if (myConcessionClerk != -1) {
			// cc is on break, find another line
			printf("Customer %d in Group %d sees ConcessionClerk %d is on break.\n",
				customerNumber, groupNumber, myConcessionClerk);
		}
		myConcessionClerk = -1;
		for(int i = 0; i < numberOfConcessionClerks; i++) {
			if (ccState[i] == C_READY) {
				myConcessionClerk = i;
				ccState[i] = C_BUSY;
				break;
			}
		}
		
		if (myConcessionClerk == -1) {
			// no one's ready
			// get in the shortest line
			int minLineNumber;
			for(int i = 0; i < numberOfConcessionClerks; i++) {
				if (ccState[i] != C_BREAK) {
					// there must be at least one tc who's not on break.
					minLineNumber = i;
					break;
				}
			}
			int minLineCount = ccLineCount[minLineNumber];
			for(int i = minLineNumber+1; i < numberOfConcessionClerks; i++) {
				if (ccState[i] != C_BREAK) {
					if (minLineCount > ccLineCount[i]) {
						minLineNumber = i;
						minLineCount = ccLineCount[i];
					}
				}
			}
			myConcessionClerk = minLineNumber;
			ccLineCount[myConcessionClerk]++;
			printf("Customer %d in Group %d is getting in ConcessionClerk line %d.\n",
				customerNumber, groupNumber, myConcessionClerk);
			int times = ccBreakTime[myConcessionClerk];
			ccLineCV[myConcessionClerk]->Wait(ccLineLock);
			if (times != ccBreakTime[myConcessionClerk]) {
				// cc is on break and off break, line up again
				printf("Customer %d in Group %d sees ConcessionClerk %d is on break.\n",
					customerNumber, groupNumber, myConcessionClerk);
				myConcessionClerk = -1;
				continue;
			}
		}
	}
	ccLineLock->Release();
	
	// interact with cc
	ccLock[myConcessionClerk]->Acquire();
	// tell cc how many tickets
	printf("Customer %d in Group %d is walking up to ConcessionClerk %d to buy %d popcorn and %d soda.\n", 
		customerNumber, groupNumber, myConcessionClerk, popcornForGroup, sodaForGroup);
	ccPopcorn[myConcessionClerk] = popcornForGroup;
	ccSoda[myConcessionClerk] = sodaForGroup;
	ccCV[myConcessionClerk]->Signal(ccLock[myConcessionClerk]);
	ccCV[myConcessionClerk]->Wait(ccLock[myConcessionClerk]);
	price = ccPrice[myConcessionClerk];
	// pay
	printf("Customer %d in Group %d in ConcessionClerk line %d is paying $%.2lf for food.\n",
		customerNumber, groupNumber, myConcessionClerk, (double)price);
	ccCV[myConcessionClerk]->Signal(ccLock[myConcessionClerk]);
	ccCV[myConcessionClerk]->Wait(ccLock[myConcessionClerk]);
	// leave
	printf("Customer %d in Group %d is leaving ConcessionClerk %d.\n",
		customerNumber, groupNumber, myConcessionClerk);
	ccCV[myConcessionClerk]->Signal(ccLock[myConcessionClerk]);
	ccLock[myConcessionClerk]->Release();
	// give ticket
	while (true) {
		// break if the ticket taker let the group in the theater room
		int myTicketTaker = -1;
		ttLineLock->Acquire();
		while (myTicketTaker == -1 || ttState[myTicketTaker] == C_BREAK) {
			if (myTicketTaker != -1) {
				// tt is on break
				printf("Customer %d in Group %d sees TicketTaker %d is on break.\n",
					customerNumber, groupNumber, myTicketTaker);
			}
			myTicketTaker = -1;
			int cont = 0;
			for(int i = 0; i < numberOfTicketTakers; i++) {
				if (ttStop[i] != 0) {
					// there's no seat
					// goto lobby
					printf("Customer %d in Group %d sees TicketTaker %d is no longer taking tickets. Going to the lobby.\n", 
						customerNumber, groupNumber, i);
					groupLock[groupNumber]->Acquire();
					printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
						customerNumber, groupNumber);
					groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
					groupLock[groupNumber]->Release();
					printf("Customer %d in Group %d is in the lobby.\n", customerNumber, groupNumber);
					lobbyCV->Wait(ttLineLock);
					printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
						customerNumber, groupNumber);
					// make sure everyone in group gets into lobby
					for(int j = 1; j < groupSize; j++) {
						groupSemaphore[groupNumber]->P();
					}
					groupLock[groupNumber]->Acquire();
					printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
						customerNumber, groupNumber);
					// make everyone leave the lobby
					groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
					groupLock[groupNumber]->Release();
					printf("Customer %d in Group %d is leaving the lobby.\n", customerNumber, groupNumber);
					printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
						customerNumber, groupNumber);
					for(int j = 1; j < groupSize; j++) {
						groupSemaphore[groupNumber]->P();
					}
					cont = 1;
					break;
				}
			}
			if (cont) continue;
			
			for(int i = 0; i < numberOfTicketTakers; i++) {
				// find a tt is not busy
				if (ttState[i] == C_READY) {
					myTicketTaker = i;
					ttState[i] = C_BUSY;
					break;
				}
			}

			if (myTicketTaker == -1) {
				// no one's ready
				// get in the shortest line
				int minLineNumber;
				for(int i = 0; i < numberOfTicketTakers; i++) {
					if (ttState[i] != C_BREAK) {
						// there must be at least one tc who's not on break.
						minLineNumber = i;
						break;
					}
				}
				// gets in shortest line
				int minLineCount = ttLineCount[minLineNumber];
				for(int i = minLineNumber+1; i < numberOfTicketTakers; i++) {
					if (ttState[i] != C_BREAK) {
						if (minLineCount > ttLineCount[i]) {
							minLineNumber = i;
							minLineCount = ttLineCount[i];
						}
					}
				}
				myTicketTaker = minLineNumber;
				ttLineCount[myTicketTaker]++;
				printf("Customer %d in Group %d is getting in TicketTaker line %d.\n",
					customerNumber, groupNumber, myTicketTaker);
				int times = ttBreakTime[myTicketTaker];
				ttLineCV[myTicketTaker]->Wait(ttLineLock);
				if (times != ttBreakTime[myTicketTaker]) {
					// the ticket taker is on break and off break, line up again
					printf("Customer %d in Group %d sees TicketTaker %d is on break.\n",
						customerNumber, groupNumber, myTicketTaker);
					myTicketTaker = -1;
					continue;
				}
			}
		}
		if (ttStop[myTicketTaker] != 0) {
			// there's no seat
			// goto lobby
			printf("Customer %d in Group %d sees TicketTaker %d is no longer taking tickets. Going to the lobby.\n", 
				customerNumber, groupNumber, myTicketTaker);
			groupLock[groupNumber]->Acquire();
			printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
				customerNumber, groupNumber);
			groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
			groupLock[groupNumber]->Release();
			printf("Customer %d in Group %d is in the lobby.\n", customerNumber, groupNumber);
			lobbyCV->Wait(ttLineLock);
			printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
				customerNumber, groupNumber);
			for(int i = 1; i < groupSize; i++) {
				groupSemaphore[groupNumber]->P();
			}
			groupLock[groupNumber]->Acquire();
			printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
				customerNumber, groupNumber);
			groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
			groupLock[groupNumber]->Release();
			printf("Customer %d in Group %d is leaving the lobby.\n", customerNumber, groupNumber);
			printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
				customerNumber, groupNumber);
			for(int i = 1; i < groupSize; i++) {
				groupSemaphore[groupNumber]->P();
			}
			continue;
		}
		ttLineLock->Release();
		// interact with tt
		ttLock[myTicketTaker]->Acquire();
		ttTicket[myTicketTaker] = groupSize;
		ttCV[myTicketTaker]->Signal(ttLock[myTicketTaker]);
		ttCV[myTicketTaker]->Wait(ttLock[myTicketTaker]);
		ttLineLock->Acquire();
		if (ttStop[myTicketTaker] != 0 || ttProcess[myTicketTaker] == 0) {
			// there's no seat
			// goto lobby
			printf("Customer %d in Group %d sees TicketTaker %d is no longer taking tickets. Going to the lobby.\n", 
				customerNumber, groupNumber, myTicketTaker);
			groupLock[groupNumber]->Acquire();
			printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
				customerNumber, groupNumber);
			groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
			groupLock[groupNumber]->Release();
			printf("Customer %d in Group %d is in the lobby.\n", customerNumber, groupNumber);
			ttLock[myTicketTaker]->Release();
			lobbyCV->Wait(ttLineLock);
			printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
				customerNumber, groupNumber);
			for(int i = 1; i < groupSize; i++) {
				groupSemaphore[groupNumber]->P();
			}
			groupLock[groupNumber]->Acquire();
			printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
				customerNumber, groupNumber);
			groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
			groupLock[groupNumber]->Release();
			printf("Customer %d in Group %d is leaving the lobby.\n", customerNumber, groupNumber);
			printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
				customerNumber, groupNumber);
			for(int i = 1; i < groupSize; i++) {
				groupSemaphore[groupNumber]->P();
			}
			ttLineLock->Release();
			continue;
		} else {
			ttLineLock->Release();
			printf("Customer %d in Group %d is leaving TicketTaker %d.\n", 
				customerNumber, groupNumber, myTicketTaker);
			ttCV[myTicketTaker]->Signal(ttLock[myTicketTaker]);
			ttLock[myTicketTaker]->Release();
			break;
		}
	}
	
	groupLock[groupNumber]->Acquire();
	// allocate seat here
	seatMutex->P();
	int allocated = 0;
	// same row
	for(int i = 0; i < 5; i++) {
		if (groupSize <= seatRow[i]) {
			allocated = 1;
			for(int j = 0; j < groupSize; j++) {
				customerSeat[customerNumber+j] = 5 - seatRow[i] + j + 5 * i;
			}
			seatRow[i] -= groupSize;
			break;
		}
	}
	if (!allocated) {
		int start = 0;
		// find a start row
		for(int i = 0; i < 5; i++) {
			int tempSeat = 0;
			for(int j = i; j < 5; j++) {
				if(seatRow[j] == 0) {
					break;
				} else {
					tempSeat += seatRow[j];
					if (tempSeat >= groupSize) break;
				}
			}
			if (tempSeat >= groupSize) {
				start = i;
				break;
			}
		}
		int seated = 0;
		// alloc seats
		for(int i = start; i < 5; i++) {
			int taken = 0;
			for(int j = 0; j < seatRow[i]; j++) {
				customerSeat[customerNumber+seated] = i*5 + 5 - seatRow[i] + j;
				seated++;
				taken++;
				if (seated == groupSize) break;
			}
			seatRow[i] -= taken;
			if (seated == groupSize) break;
		}
	}
	seatMutex->V();
	printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
		customerNumber, groupNumber);
	groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
	printf("Customer %d in Group %d has found the following seat: row %d and seat %d.\n",
		customerNumber, groupNumber, customerSeat[customerNumber]/5, customerSeat[customerNumber]%5);
	groupLock[groupNumber]->Release();
	
	// see the movie
	movieLock->Acquire();
	printf("Customer %d in Group %d in sitting in a theater room seat.\n",
		customerNumber, groupNumber);
	customerReadyWatchingMovie++;
	movieCV->Wait(movieLock);
	customerReadyWatchingMovie--;
	printf("Customer %d in Group %d in getting out of a theater room seat.\n",
		customerNumber, groupNumber);
	movieLock->Release();	
	
	// wait for the customers tell him if they are going to the bathroom
	printf("HeadCustomer %d of Group %d is waiting for the group to form.\n",
		customerNumber, groupNumber);
	for(int i = 1; i < groupSize; i++) {
		groupSemaphore[groupNumber]->P();
	}
	// decide if go to bathroom
	int bathroom = rand() % 4;
	if (bathroom == 0) {
		// go to the bathroom
		bathroomSemaphore->P();
		printf("Customer %d in Group %d is going to the bathroom.\n",
			customerNumber, groupNumber);
		int bathroomTime = rand() % 5;
		for(int i = 0; i < bathroomTime; i++) {
			currentThread->Yield();
		}
		printf("Customer %d in Group %d is leaving to the bathroom.\n",
			customerNumber, groupNumber);
		bathroomSemaphore->V();
	}
	printf("Customer %d in Group %d is in the lobby.\n", customerNumber, groupNumber);
	for(int i = 0; i < customerBathroomNumber[groupNumber]; i++) {
		groupBathroomSemaphore[groupNumber]->P();
	}
	// every one's here, wake them up and leave
	
	groupLock[groupNumber]->Acquire();
	printf("HeadCustomer %d of Group %d has told the group to proceed.\n",
		customerNumber, groupNumber);
	groupCV[groupNumber]->Broadcast(groupLock[groupNumber]);
	printf("Customer %d in Group %d is leaving the lobby.\n", customerNumber, groupNumber);
	groupLock[groupNumber]->Release();
	
	// all set, leave
	leaveMutex->P();
	leaveCount++;
	printf("Customer %d in Group %d has left the movie theater.\n", 
		customerNumber, groupNumber);
	leaveMutex->V();
}

/*
 * TicketTakerBehavior
 * controls customer if there's not enough seats in the theater room
 * commucates with HC and manager
 */
void TicketTakerBehavior(int data) {
	while (true) {
		ttLineLock->Acquire();
		if (ttBreak[data] != 0) {
			// go on a break
			// wake all customers
			ttState[data] = C_BREAK;
			printf("TicketTaker %d is going on break.\n", data);
			ttBreakTime[data]++;
			ttLineCount[data] = 0;
			ttLineCV[data]->Broadcast(ttLineLock);
			ttBreakCV->Wait(ttLineLock);
			ttState[data] = C_BUSY;
			printf("TicketTaker %d is coming off break.\n", data);
			ttBreak[data] = 0;
		}
		if (ttLineCount[data] > 0) {
			// there's someone in line
			// signal a customer
			printf("TicketTaker %d has a line length %d and is signaling a customer.\n",
				data, ttLineCount[data]);
			ttLineCount[data]--;
			ttState[data] = C_BUSY;
			ttLineCV[data]->Signal(ttLineLock);
		} else {
			// no one in line
			// set ready
			ttState[data] = C_READY;
			printf("TicketTaker %d has no one in line. I am available for a customer.\n",
				data);
			for(int i = 0; i < numberOfTicketTakers; i++) {
				if (ttStop[i] != 0) {
					ttStop[data] = 1;
					break;
				}
			}
		}
		ttLock[data]->Acquire();
		ttLineLock->Release();
		ttCV[data]->Wait(ttLock[data]);
		ttLineLock->Acquire();
		if (ttState[data] == C_BREAK) {
			ttLock[data]->Release();
			// waken up by manager
			// go on a break
			printf("TicketTaker %d is going on break.\n", data);
			ttBreakTime[data]++;
			ttBreakCV->Wait(ttLineLock);
			printf("TicketTaker %d is coming off break.\n", data);
			ttState[data] = C_BUSY;
			ttBreak[data] = 0;
			ttLineLock->Release();
			continue;
		}
		ttProcess[data] = 0;
		// get ticket number
		// allocate seat
		int seats = ttTicket[data];
		printf("TicketTaker %d has received %d tickets.\n", data, seats);
		ttLineLock->Acquire();
		if (ttStop[data] != 0 || seats > ttRemainSeat) {
			// no seats left
			// tell everyone in his line to wait in the lobby
			printf("TicketTaker %d is not allowing the group into the theater. The number of taken tickets is %d and the group size is %d.\n", data, 25-ttRemainSeat, seats);
			ttStop[data] = 1;
			for(int i = 0; i < numberOfTicketTakers; i++) {
				if (ttState[i] == C_READY) {
					ttStop[i] = 1;
					break;
				}
			}
			ttLineCV[data]->Broadcast(ttLineLock);
			// stop selling tickets
			ttLineCount[data] = 0;
			printf("TicketTaker %d has stopped taking tickets.\n", data);
			ttCV[data]->Signal(ttLock[data]);
			ttLock[data]->Release();
			lobbyCV->Wait(ttLineLock);
			ttLineLock->Release();
			continue;
		} else {
			// seats is enough
			printf("TicketTaker %d is allowing the group into the theater. The number of tickets taken is %d.\n",
				data, 25-ttRemainSeat);
			ttProcess[data] = 1;
			ttRemainSeat -= seats;
			ttTicketTotal += seats;
			ttLineLock->Release();
		}
		ttCV[data]->Signal(ttLock[data]);
		ttCV[data]->Wait(ttLock[data]);
		ttLock[data]->Release();
	}
}

/*
 * MovieTechnicianBehavior
 * start/finish movie
 * manager tell him to start the movie
 * when a movie is finished, tell everyone to leave
 */
void MovieTechnicianBehavior(int data) {
	movieLock->Acquire();
	movieState = M_WAITING;
	movieLock->Release();
	while (true) {
		mtLock->Acquire();
		mtCV->Wait(mtLock);
		// told by manager to start the movie
		movieState = M_STARTED;
		mtLock->Release();
		printf("The MovieTechnician has started the movie.\n");
		int movieTime = rand() % 100 + 200;
		// playing the movie
	    for(int i = 0; i < movieTime; i++) {
	      currentThread->Yield();
	    }
		printf("The MovieTechnician has ended the movie.\n");
	    movieLock->Acquire();
		// movie's finished
	    // broadcast all customers
		printf("The MovieTechnician has told all customers to leave the theater room.\n");
	    movieCV->Broadcast(movieLock);
		movieState = M_FINISHED;
	    movieLock->Release();
	}
}

/*
 * TicketClerkBehavior
 * ticket clerk sells tickets
 * commucate with HC and manager only
 * keep track in his own incomes
 */
void TicketClerkBehavior(int data) {
	while (true) {
		tcLineLock->Acquire();
		if (tcBreak[data] != 0) {
			// go on a break
			// wake all customers
			tcState[data] = C_BREAK;
			printf("TicketClerk %d is going on break.\n", data);
			tcBreakTime[data]++;
			tcLineCount[data] = 0;
			tcLineCV[data]->Broadcast(tcLineLock);
			tcBreakCV->Wait(tcLineLock);
			tcState[data] = C_BUSY;
			printf("TicketClerk %d is coming off break.\n", data);
			tcBreak[data] = 0;
		}
		if (tcLineCount[data] > 0) {
			// there's someone in line
			// signal a customer
			printf("TicketClerk %d has a line length %d and is signaling a customer.\n",
				data, tcLineCount[data]);
			tcLineCount[data]--;
			tcState[data] = C_BUSY;
			tcLineCV[data]->Signal(tcLineLock);
		} else {
			// no one in line
			// set ready
			tcState[data] = C_READY;
			printf("TicketClerk %d has no one in line. I am available for a customer.\n",
				data);
		}
		tcLock[data]->Acquire();
		tcLineLock->Release();
		tcCV[data]->Wait(tcLock[data]);
		tcLineLock->Acquire();
		if (tcState[data] == C_BREAK) {
			tcLock[data]->Release();
			// waken up by manager
			// go on a break
			printf("TicketClerk %d is going on break.\n", data);
			tcBreakTime[data]++;
			tcBreakCV->Wait(tcLineLock);
			printf("TicketClerk %d is coming off break.\n", data);
			tcState[data] = C_BUSY;
			tcBreak[data] = 0;
			tcLineLock->Release();
			continue;
		}
		tcLineLock->Release();
		// interact with the head customer
		tcPrice[data] = tcTicket[data] * TICKET_PRICE;
		printf("TicketClerk %d has an order for %d and the cost is $%.2lf.\n",
			data, tcTicket[data], (double)tcPrice[data]);
		tcCV[data]->Signal(tcLock[data]);
		tcCV[data]->Wait(tcLock[data]);
		// get the money
		incomeMutex->P();
		tcIncome[data] += tcPrice[data];
		totalIncome += tcPrice[data];
		incomeMutex->V();
		tcCV[data]->Signal(tcLock[data]);
		tcCV[data]->Wait(tcLock[data]);
		tcLock[data]->Release();
	}
}

/*
 * ConcessionClerkBehavior
 * concession clerk sells food
 * commucate with HC and manager only
 * keep track in his own incomes
 */
void ConcessionClerkBehavior(int data) {
	while (true) {
		ccLineLock->Acquire();
		if (ccBreak[data] != 0) {
			// go on a break
			// wake all customers
			ccState[data] = C_BREAK;
			printf("ConcessionClerk %d is going on break.\n", data);
			ccBreakTime[data]++;
			ccLineCount[data] = 0;
			ccLineCV[data]->Broadcast(ccLineLock);
			ccBreakCV->Wait(ccLineLock);
			ccState[data] = C_BUSY;
			printf("ConcessionClerk %d is coming off break.\n", data);
			ccBreak[data] = 0;
		}
		if (ccLineCount[data] > 0) {
			// there's someone in line
			// signal a customer
			printf("ConcessionClerk %d has a line length %d and is signaling a customer.\n",
				data, ccLineCount[data]);
			ccLineCount[data]--;
			ccState[data] = C_BUSY;
			ccLineCV[data]->Signal(ccLineLock);
		} else {
			// no one in line
			// set ready
			ccState[data] = C_READY;
			printf("ConcessionClerk %d has no one in line. I am available for a customer.\n",
				data);
		}
		ccLock[data]->Acquire();
		ccLineLock->Release();
		ccCV[data]->Wait(ccLock[data]);
		if (ccState[data] == C_BREAK) {
			// waken up by manager
			// go on a break
			ccLock[data]->Release();
			printf("ConcessionClerk %d is going on break.\n", data);
			ccBreakTime[data]++;
			ccBreakCV->Wait(ccLineLock);
			printf("ConcessionClerk %d is coming off break.\n", data);
			ccState[data] = C_BUSY;
			ccBreak[data] = 0;
			ccLineLock->Release();
			continue;
		}
		// interact with the head customer
		ccPrice[data] = ccPopcorn[data] * POPCORN_PRICE + ccSoda[data] * SODA_PRICE;
		printf("ConcessionClerk %d has an order for %d popcorn and %d soda. The cost is $%.2lf.\n",
			data, ccPopcorn[data], ccSoda[data], (double)ccPrice[data]);
		ccCV[data]->Signal(ccLock[data]);
		ccCV[data]->Wait(ccLock[data]);
		// get the money
		incomeMutex->P();
		ccIncome[data] += ccPrice[data];
		totalIncome += ccPrice[data];
		incomeMutex->V();
		ccCV[data]->Signal(ccLock[data]);
		ccCV[data]->Wait(ccLock[data]);
		ccLock[data]->Release();
	}
}

/* 
 * Manager Behavior
 * collect money
 * make employees on/off break
 * start/finish movie
 * if all customer leaves the theater, exit
 */
void ManagerBehavior(int data) {
	while (true) {
		// break
		int tc = 0;
		int tcb = 0;
		// tc on break
		// tc no one in line
		tcLineLock->Acquire();
		for(int i = 0; i < numberOfTicketClerks; i++) {
			if (tcBreak[i] == 0) tc++;
		}
		if (tc > 1) {
			// make sure someone's working
			for(int i = 0; i < numberOfTicketClerks; i++) {
				// no one in line
				if (tcState[i] == C_READY) {
					int b = rand() % 5;
					if (b == 0) {
						tcLock[i]->Acquire();
						tcState[i] = C_BREAK;
						tcBreak[i] = 1;
						printf("Manager has told TicketClerk %d to go on break.\n", i);
						tcCV[i]->Signal(tcLock[i]);
						tcLock[i]->Release();
						tcb = 1;
						break;
					}
				}
			}
			if (!tcb) {
				// tc less than 3 in line
				for(int i = 0; i < numberOfTicketClerks; i++) {
					if (tcState[i] != C_BREAK && tcLineCount[i] < 3) {
						int b = rand() % 5;
						if (b == 0) {
							tcBreak[i] = 1;
							printf("Manager has told TicketClerk %d to go on break.\n", i);
							tcb = 1;
							break;
						}
					}
				}
			}
 		}
		tcLineLock->Release();
		// tc off break
		tc = 0;
		tcLineLock->Acquire();
		for(int i = 0; i < numberOfTicketClerks; i++) {
			if (tcState[i] == C_BREAK) tc++;
		}
		if (tc) {
			for(int i = 0; i < numberOfTicketClerks; i++) {
				if (tcLineCount[i] >= 5) {
					// make any tc off break
					tcBreakCV->Signal(tcLineLock);
					break;
				}
			}
		}
		tcLineLock->Release();
		
		// cc break
		int cc = 0;
		int ccb = 0;
		// cc on break
		// cc no one in line
		ccLineLock->Acquire();
		for(int i = 0; i < numberOfConcessionClerks; i++) {
			if (ccBreak[i] == 0) cc++;
		}
		if (cc > 1) {
			for(int i = 0; i < numberOfConcessionClerks; i++) {
				if (ccState[i] == C_READY) {
					int b = rand() % 5;
					if (b == 0) {
						ccLock[i]->Acquire();
						ccState[i] = C_BREAK;
						ccBreak[i] = 1;
						printf("Manager has told ConcessionClerk %d to go on break.\n", i);
						ccCV[i]->Signal(ccLock[i]);
						ccLock[i]->Release();
						ccb = 1;
						break;
					}
				}
			}
			if (!ccb) {
				// cc less than 3 in line
				for(int i = 0; i < numberOfConcessionClerks; i++) {
					if (ccState[i] != C_BREAK && ccLineCount[i] < 3) {
						int b = rand() % 5;
						if (b == 0) {
							ccBreak[i] = 1;
							printf("Manager has told ConcessionClerk %d to go on break.\n", i);
							ccb = 1;
							break;
						}
					}
				}
			}
 		}
		ccLineLock->Release();
		// cc off break
		cc = 0;
		ccLineLock->Acquire();
		for(int i = 0; i < numberOfConcessionClerks; i++) {
			if (ccState[i] == C_BREAK) cc++;
		}
		if (cc) {
			for(int i = 0; i < numberOfConcessionClerks; i++) {
				if (ccLineCount[i] >= 5) {
					ccBreakCV->Signal(ccLineLock);
					break;
				}
			}
		}
		ccLineLock->Release();
		
		// tt break
		int tt = 0;
		int ttb = 0;
		// tt on break
		// tt no one in line
		ttLineLock->Acquire();
		for(int i = 0; i < numberOfTicketTakers; i++) {
			if (ttBreak[i] == 0) tt++;
		}
		if (tt > 1) {
			for(int i = 0; i < numberOfTicketTakers; i++) {
				if (ttState[i] == C_READY) {
					int b = rand() % 5;
					if (b == 0) {
						ttLock[i]->Acquire();
						ttState[i] = C_BREAK;
						ttBreak[i] = 1;
						printf("Manager has told TicketTakers %d to go on break.\n", i);
						ttCV[i]->Signal(ttLock[i]);
						ttLock[i]->Release();
						ttb = 1;
						break;
					}
				}
			}
			if (!ttb) {
				// tt less than 3 in line
				for(int i = 0; i < numberOfTicketTakers; i++) {
					if (ttState[i] != C_BREAK && ttLineCount[i] < 3) {
						int b = rand() % 5;
						if (b == 0) {
							ttBreak[i] = 1;
							printf("Manager has told ConcessionClerk %d to go on break.\n", i);
							ttb = 1;
							break;
						}
					}
				}
			}
 		}
		ttLineLock->Release();
		// tt off break
		tt = 0;
		ttLineLock->Acquire();
		for(int i = 0; i < numberOfTicketTakers; i++) {
			if (ttState[i] == C_BREAK) tt++;
		}
		if (tt) {
			for(int i = 0; i < numberOfTicketTakers; i++) {
				if (ttLineCount[i] >= 5) {
					ttBreakCV->Signal(ttLineLock);
					break;
				}
			}
		}
		ttLineLock->Release();
		
		// collect money
		// start movie
		ttLineLock->Acquire();
		movieLock->Acquire();
		int start = 1;
		for (int i = 0; i < numberOfTicketTakers; i++) {
			if (ttStop[i] == 0 && ttStop[i] == C_BUSY) {
				start = 0;
				break;
			}
		}
		if (ttTicketTotal == numberOfCustomers) {
			// no customers
			start = 1;
		}
		if (customerReadyWatchingMovie + ttRemainSeat != SEATS || customerReadyWatchingMovie == 0) {
			// make sure all customers sit
			start = 0;
		}
		if (movieState != M_WAITING) {
			// movie is not ready to start
			start = 0;
		}
		if (start != 0) {
			// start the movie
			mtLock->Acquire();
			printf("Manager is telling the MovieTechnician to start the movie.\n");
			mtCV->Signal(mtLock);
			mtLock->Release();
		}
		movieLock->Release();
		ttLineLock->Release();
		// finish movie
		ttLineLock->Acquire();
		movieLock->Acquire();
		if (movieState == M_FINISHED) {
			if (customerReadyWatchingMovie == 0) {
				// all customers leaves
				// make movie waiting
				movieState = M_WAITING;
				// make ticket taker start taking
				for(int i = 0; i < numberOfTicketTakers; i++) {
					ttStop[i] = 0;
				}
				//wake up all customers in the lobby
				ttRemainSeat = SEATS;
				seatMutex->P();
				for(int i = 0; i < 5; i++)
					seatRow[i] = 5;
				seatMutex->V();
				lobbyCV->Broadcast(ttLineLock);
			}
		}
		movieLock->Release();
		ttLineLock->Release();
		// if all customers leave
		leaveMutex->P();
		if (numberOfCustomers == leaveCount) {
			incomeMutex->P();
			printf("Total money made by office $%.2lf\n", (double)totalIncome);
			for(int i = 0; i < numberOfTicketClerks; i++) {
				printf("Manager collected $%.2lf from TicketClerk %d\n",(double)tcIncome[i],i);
			}
			for(int i = 0; i < numberOfConcessionClerks; i++) {
				printf("Manager collected $%.2lf from ConcessionClerk %d\n",(double)ccIncome[i],i);
			}
			incomeMutex->V();
			leaveMutex->V();
			break;
		}
		leaveMutex->V();
		// collect money
		incomeMutex->P();
		printf("Total money made by office $%.2lf\n", (double)totalIncome);
		for(int i = 0; i < numberOfTicketClerks; i++) {
			printf("Manager collected $%.2lf from TicketClerk %d\n",(double)tcIncome[i],i);
		}
		for(int i = 0; i < numberOfConcessionClerks; i++) {
			printf("Manager collected $%.2lf from ConcessionClerk %d\n",(double)ccIncome[i],i);
		}
		incomeMutex->V();
		// wait time
		int wait = 50;
		for(int i = 0; i < wait; i++) {
			currentThread->Yield();
		}
	}
}

// fixed number of threads
void Problem2() {
	numberOfGroups = 10;
	numberOfConcessionClerks = 5;
	numberOfTicketClerks = 2;
	numberOfTicketTakers = 3;
	int* groupSize = new int[numberOfGroups];
	numberOfCustomers = 0;
	for(int i = 0; i < numberOfGroups; i++) {
		groupSize[i] = i % 5 + 1;
		numberOfCustomers += groupSize[i];
	}
	Init();
	int ids = 0;
	printf("Number of Customers = %d\n", numberOfCustomers);
	printf("Number of Groups = %d\n", numberOfGroups);
	printf("Number of TicketClerks = %d\n", numberOfTicketClerks);
	printf("Number of ConcessionClerks = %d\n", numberOfConcessionClerks);
	printf("Number of TicketTakers = %d\n", numberOfTicketTakers);
	
	// fork customers
	for(int i = 0; i < numberOfGroups; i++) {
		CustomerInfo *info = new CustomerInfo;
		info->customerNumber = ids++;
		info->groupNumber = i;
		info->groupSize = groupSize[i];
		Thread *t = new Thread("hc");
		t->Fork(HeadCustomerBehavior, (int)info);
		for(int j = 1; j < groupSize[i]; j++) {
	      CustomerInfo *info1 = new CustomerInfo;
	      info1->customerNumber = ids++;
	      info1->groupNumber = i;
	      info1->groupSize = groupSize[i];
	      Thread *t1 = new Thread("c");
	      t1->Fork(CustomerBehavior, (int) info1);
	    }
	}
	
	// fork tc
	for(int i = 0; i < numberOfTicketClerks; i++) {
		Thread *t = new Thread("tc");
		t->Fork(TicketClerkBehavior, i);
	}
	
	// fork cc
	for(int i = 0; i < numberOfConcessionClerks; i++) {
		Thread *t = new Thread("cc");
		t->Fork(ConcessionClerkBehavior, i);
	}
	
	// fork tt
	for(int i = 0; i < numberOfTicketTakers; i++) {
		Thread *t = new Thread("tt");
		t->Fork(TicketTakerBehavior, i);
	}
	
	// fork mt
	Thread *mtThread = new Thread("mt");
	mtThread->Fork(MovieTechnicianBehavior, 0);
	
	// fork m
	Thread *managerThread = new Thread("m");
	managerThread ->Fork(ManagerBehavior, 0);
}

// user input number of threads
void SystemTest() {
	printf("How many Groups? ");
	scanf("%d", &numberOfGroups);
	printf("How many customers in each group? ");
	int* groupSize = new int[numberOfGroups];
	numberOfCustomers = 0;
	for(int i = 0; i < numberOfGroups; i++) {
		scanf("%d", &groupSize[i]);
		numberOfCustomers += groupSize[i];
	}
	printf("How many TicketClerks? ");
	scanf("%d", &numberOfTicketClerks);
	printf("How many ConcessionClerks? ");
	scanf("%d", &numberOfConcessionClerks);
	printf("How many TicketTakers? ");
	scanf("%d", &numberOfTicketTakers);
	Init();
	int ids = 0;
	printf("Number of Customers = %d\n", numberOfCustomers);
	printf("Number of Groups = %d\n", numberOfGroups);
	printf("Number of TicketClerks = %d\n", numberOfTicketClerks);
	printf("Number of ConcessionClerks = %d\n", numberOfConcessionClerks);
	printf("Number of TicketTakers = %d\n", numberOfTicketTakers);
	
	// fork customers
	for(int i = 0; i < numberOfGroups; i++) {
		CustomerInfo *info = new CustomerInfo;
		info->customerNumber = ids++;
		info->groupNumber = i;
		info->groupSize = groupSize[i];
		Thread *t = new Thread("hc");
		t->Fork(HeadCustomerBehavior, (int)info);
		for(int j = 1; j < groupSize[i]; j++) {
	      CustomerInfo *info1 = new CustomerInfo;
	      info1->customerNumber = ids++;
	      info1->groupNumber = i;
	      info1->groupSize = groupSize[i];
	      Thread *t1 = new Thread("c");
	      t1->Fork(CustomerBehavior, (int) info1);
	    }
	}
	
	// fork tc
	for(int i = 0; i < numberOfTicketClerks; i++) {
		Thread *t = new Thread("tc");
		t->Fork(TicketClerkBehavior, i);
	}
	
	// fork cc
	for(int i = 0; i < numberOfConcessionClerks; i++) {
		Thread *t = new Thread("cc");
		t->Fork(ConcessionClerkBehavior, i);
	}
	
	// fork tt
	for(int i = 0; i < numberOfTicketTakers; i++) {
		Thread *t = new Thread("tt");
		t->Fork(TicketTakerBehavior, i);
	}
	
	// fork mt
	Thread *mtThread = new Thread("mt");
	mtThread->Fork(MovieTechnicianBehavior, 0);
	
	// fork m
	Thread *managerThread = new Thread("m");
	managerThread ->Fork(ManagerBehavior, 0);
}

// random number of threads
void RandomTest() {
	numberOfGroups = rand() % 100 + 1;
	numberOfConcessionClerks = rand() % 5 + 1;
	numberOfTicketClerks = rand() % 5 + 1;
	numberOfTicketTakers = rand() % 3 + 1;
	int* groupSize = new int[numberOfGroups];
	numberOfCustomers = 0;
	for(int i = 0; i < numberOfGroups; i++) {
		groupSize[i] = rand() % 5 + 1;
		numberOfCustomers += groupSize[i];
	}	Init();
	int ids = 0;
	printf("Number of Customers = %d\n", numberOfCustomers);
	printf("Number of Groups = %d\n", numberOfGroups);
	printf("Number of TicketClerks = %d\n", numberOfTicketClerks);
	printf("Number of ConcessionClerks = %d\n", numberOfConcessionClerks);
	printf("Number of TicketTakers = %d\n", numberOfTicketTakers);
	
	// fork customers
	for(int i = 0; i < numberOfGroups; i++) {
		CustomerInfo *info = new CustomerInfo;
		info->customerNumber = ids++;
		info->groupNumber = i;
		info->groupSize = groupSize[i];
		Thread *t = new Thread("hc");
		t->Fork(HeadCustomerBehavior, (int)info);
		for(int j = 1; j < groupSize[i]; j++) {
	      CustomerInfo *info1 = new CustomerInfo;
	      info1->customerNumber = ids++;
	      info1->groupNumber = i;
	      info1->groupSize = groupSize[i];
	      Thread *t1 = new Thread("c");
	      t1->Fork(CustomerBehavior, (int) info1);
	    }
	}
	
	// fork tc
	for(int i = 0; i < numberOfTicketClerks; i++) {
		Thread *t = new Thread("tc");
		t->Fork(TicketClerkBehavior, i);
	}
	
	// fork cc
	for(int i = 0; i < numberOfConcessionClerks; i++) {
		Thread *t = new Thread("cc");
		t->Fork(ConcessionClerkBehavior, i);
	}
	
	// fork tt
	for(int i = 0; i < numberOfTicketTakers; i++) {
		Thread *t = new Thread("tt");
		t->Fork(TicketTakerBehavior, i);
	}
	
	// fork mt
	Thread *mtThread = new Thread("mt");
	mtThread->Fork(MovieTechnicianBehavior, 0);
	
	// fork m
	Thread *managerThread = new Thread("m");
	managerThread->Fork(ManagerBehavior, 0);
}
